/*$Copyright:
 * Copyright (C) 1992.5.22. Recruit Co.,Ltd. 
 * Institute for Supercomputing Research
 * All rights reserved.
 * NewsBase  by ISR, Kazuto MIYAI, Gary ARAKAKI, Katsunori SUZUKI, Kok-meng Lue
 *
 * You may freely copy, distribute and reuse the code in this program under 
 * following conditions.
 * - to include this notice in the source code, if it is to be distributed 
 *   with source code.
 * - to add the file named "COPYING" within the code, which shall include 
 *   GNU GENERAL PUBLIC LICENSE(*).
 * - to display an acknowledgement in binary code as follows: "This product
 *   includes software developed by Recruit Co.,Ltd., ISR."
 * - to display a notice which shall state that the users may freely copy,
 *   distribute and reuse the code in this program under GNU GENERAL PUBLIC
 *   LICENSE(*)
 * - to indicate the way to access the copy of GNU GENERAL PUBLIC LICENSE(*)
 *
 *   (*)GNU GENERAL PUBLIC LICENSE is stored in the file named COPYING
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
$*/
/* InfoD.m */

#import "InfoD.h"
#import <appkit/Panel.h>
#import <objc/NXStringTable.h>
#import <objc/objc.h>
#import <objc/hashtable.h>

#import <string.h>
#import <libc.h>
#import <ctype.h>
#import <streams/streams.h>
#import "data_types.h"
#import "errdebug.h"

#define		DEF_STRT_SIZE		16
#define		DEF_INTT_SIZE		16
#define		STRING_BUF_MAX		256

@implementation InfoD

static NXAtom fileExtension;

+ initialize
{
    fileExtension = NXUniqueStringNoCopy("tbl");
    return(self);
}

+ (NXAtom)rtfControlWord
{
    return(NULL);
}

+ (NXAtom)fileExtension
{
    return(fileExtension);
}

+ (NXAtom)pasteboardType
{
    return(NULL);
}

- initWithKey:(const char *)theKey strCapacity:(int)kstr_num 
                          intCapacity:(int)kint_num
{
    [super initKeyDesc:"*" valueDesc:"!" capacity:kstr_num + kint_num];
    key = NXCopyStringBufferFromZone(theKey, [self zone]);
    zone = [self zone];
    return(self);
}

- initWithKey:(const char *)theKey
{
    [self initWithKey:theKey strCapacity:DEF_STRT_SIZE intCapacity:DEF_INTT_SIZE];
    return self;
}

- init
{
    
    [self initWithKey:"" strCapacity:DEF_STRT_SIZE intCapacity:DEF_INTT_SIZE];
    return self;
}

- (const char *)key
{
    return(key);
}

- addInfo:(char *)kvalue key:(char *)theKey
{
    return([self addInfoString:kvalue key:theKey]);
}

- addInfoString:(const char *)theValue key:(const char *)theKey
{
    char *copyOfValue;
    char *copyOfKey;
    
    // save type of value in 4 byte prefix to key
    copyOfKey = NXZoneMalloc(zone, strlen(theKey) + 5);
    *copyOfKey = '*';
    copyOfKey += 4;
    strcpy(copyOfKey, theKey);
    copyOfValue = NXCopyStringBufferFromZone(theValue, zone);
    [super insertKey:(const void *)copyOfKey value:(void *)copyOfValue];
    return(self);
}

- addInfoInt:(int)theValue key:(char *)theKey
{
    char *copyOfKey;

    // save type of value in 4 byte prefix to key
    copyOfKey = NXZoneMalloc(zone, strlen(theKey) + 5);
    *copyOfKey = 'i';
    copyOfKey += 4;
    strcpy(copyOfKey, theKey);
    [super insertKey:(const void *)copyOfKey value:(void *)theValue];
    return(self);
}

- setHeaderInfo:(char *)kheader
{
    NXStream	*stream;
    char	theKey[STRING_BUF_MAX], value[STRING_BUF_MAX];
    
    stream = NXOpenMemory( NULL, 0, NX_READWRITE );
    NXWrite( stream, (const void *)kheader, strlen(kheader) );
    NXSeek( stream, 0, NX_FROMSTART );
    
    while( NXGetc(stream) != (-1) ){
	NXUngetc( stream );
	NXScanf( stream, "%[^: ]", theKey );
        NXGetc( stream );				/* ':' */
        NXGetc( stream );				/* ' ' */
	NXScanf( stream, "%[^\r\n]", value );
        NXGetc( stream );				/* '\r' */
        NXGetc( stream );				/* '\n' */
	[self addInfoString:value key:theKey];
    }

    return self;
}

- (void *)infoForKey:(const char *)theKey
{
    return([super valueForKey:theKey]);
}

- (BOOL)nextState:(NXHashState *)aState key:(const void **)aKey
     value:(void **)aValue
{
    BOOL status;
    static char value[16];

    status = [super nextState:aState key:aKey value:aValue];
    if (status == YES) {
        // always return string use prefix to determine type
        if (*(*(const char **)aKey - 4) == 'i') {
             sprintf(value, "%d", *aValue);
             *aValue = value;
        }
    }
    return(status);
}

/*
   readFromStream: initializes self from stream.  The stream must be in
   the format specified by RFC822.  readFromStream: returns YES if successful.
   Otherwise readFromStream: returns NO.  Failures occur if the stream is not
   in RFC822 format or there is insufficient space in the buffers.  Lines
   may not be longer than 512 characters.

   modified to accept lf in place of cr/lf.
*/

#define KEY_MAX_SIZE 256
#define VALUE_MAX_SIZE 2048

- (BOOL)readFromStream:(NXStream *)stream
{
    char        theKey[KEY_MAX_SIZE];
    char        value[VALUE_MAX_SIZE], *valueEnd;
    char	*ptr;
    int		nextChar;
    int		isNumber;
    BOOL        eos;

    while ((eos = NXAtEOS(stream)) == FALSE &&
        (nextChar = NXGetc(stream)) != '\r' && nextChar != '\n') {
        NXUngetc(stream);
        if ((NXScanf(stream, "%255[^:]: %511[^\r\n]", theKey, value)) != 2) {
            return(NO);
        }
        // consume either cr/lf or lf as line terminator
        if ((nextChar = NXGetc(stream)) != '\n') {
            if (nextChar != '\r' || NXGetc(stream) != '\n') {
                return(NO);
            }
        }
        // read continuation lines if any
        while ((nextChar = NXGetc(stream)) == ' ' || nextChar == '\t') {
            valueEnd = index(value, '\0');
            *valueEnd++ = ' ';
            if (valueEnd + 511 > value + VALUE_MAX_SIZE) {
                return(NO);
            }
            NXScanf(stream, " %511[^\r\n]", valueEnd);
            // consume either cr/lf or lf as line terminator
            if ((nextChar = NXGetc(stream)) != '\n') {
                if (nextChar != '\r' || NXGetc(stream) != '\n') {
                    return(NO);
                }
            }
        }
        NXUngetc(stream);
        DBG(1, fprintf(stderr, "%s %s", theKey, value););

        isNumber = 1;
        for (ptr = value; *ptr != '\0'; ++ptr) {
            if ((isNumber = isdigit(*ptr)) == 0) {
                break;
            }
        }
        if (isNumber != 0) {
            [self addInfoInt:atoi(value) key:theKey];
        } else {
            [self addInfoString:value key:theKey];
        }
    }
    if (eos == FALSE && nextChar == '\r'
        && NXGetc(stream) != '\n') {
        return(NO);
    }
    return(YES);
}

- (BOOL)readFromTarStream:(NXStream *)stream
{
    return([self readFromStream:stream]);
}

- (BOOL)readFromFile:(const char *)pathName
{
    NXStream *stream;
    BOOL status;

    if ((stream = NXMapFile(pathName, NX_READONLY)) != NULL) {
        status = [self readFromStream:stream];
        NXCloseMemory(stream, NX_FREEBUFFER);
        return(status);
    } else {
        return(NO);
    }
}


- (void)writeToStream:(NXStream *)stream
{
    NXHashState state;
    char *theKey, *value;

    state = [self initState];
    while ([super nextState: &state key:(const void**)&theKey
        value:(void **)&value]) {
        if (*(theKey - 4) == '*') {
             NXPrintf(stream, "%s: %s\r\n", theKey, value);
        } else if (*(theKey - 4) == 'i') {
             NXPrintf(stream, "%s: %d\r\n", theKey, value);
        }
    }
    NXPutc(stream, '\r');
    NXPutc(stream, '\n');
}

- (void)writeToTarStream:(NXStream *)stream
{
    [self writeToStream:stream];
}

- free
{
    NXHashState state;
    char *theKey, *value;

    state = [self initState];
    while ([super nextState: &state key:(const void**)&theKey
        value:(void **)&value]) {
        if (*(theKey - 4) == '*') {
            NXZoneFree(zone, theKey - 4);
            NXZoneFree(zone, value);
        } else if (*(theKey - 4) == 'i') {
            NXZoneFree(zone, theKey - 4);
        }
    }
    NXZoneFree(zone, key);
    return([super free]);
}

@end
